package com.taiyuan.zfmvp.filter;

import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONObject;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.context.annotation.Configuration;
import org.springframework.http.HttpStatus;

import javax.servlet.Filter;
import javax.servlet.FilterChain;
import javax.servlet.FilterConfig;
import javax.servlet.ServletException;
import javax.servlet.ServletRequest;
import javax.servlet.ServletResponse;
import javax.servlet.annotation.WebFilter;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.io.UnsupportedEncodingException;
import java.net.URLDecoder;
import java.util.Enumeration;
import java.util.HashMap;
import java.util.Map;
import java.util.Set;

/**
 * @param
 * @Author: zhoufang@si-tech.com.cn
 * @Descriiption: sql防注入过滤器
 * @Date 2022/11/19
 * @return
 */
@Configuration
@WebFilter(urlPatterns = "/*", filterName = "sqlFilter")
public class CommonSqlFilter implements Filter {

    private static final Logger logger = LoggerFactory.getLogger(CommonSqlFilter.class.getName());

    private static final String SQL_REG_EXP = ".*(\\b(select|insert|into|update|delete|from|where|and|or|trancate" +
            "|drop|execute|like|grant|use|union|order|by)\\b).*";// 过滤掉的sql关键字，特殊字符前面需要加\\进行转义

    @Override
    public void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain filterChain) throws IOException, ServletException {


        HttpServletRequest request = (HttpServletRequest) servletRequest;
        CustomRequestWrapper requestWrapper = new CustomRequestWrapper(request);
        Map<String, Object> parameterMap = new HashMap<>();
        parameterMap =getParameterMap(parameterMap, request, requestWrapper);
        // 正则校验是否有SQL关键字
        for (Object obj : parameterMap.entrySet()) {
            Map.Entry entry = (Map.Entry) obj;
            Object value = entry.getValue();
            if (value != null) {
                boolean isValid = isSqlInject(value.toString(), servletResponse);
                if (!isValid) {
                    return;
                }
            }
        }
        filterChain.doFilter(requestWrapper, servletResponse);


    }


    private Map<String, Object> getParameterMap(Map<String, Object> paramMap, HttpServletRequest request, CustomRequestWrapper requestWrapper) {
        // 1.POST请求获取参数
        if ("POST".equals(request.getMethod().toUpperCase())) {
            String body = requestWrapper.getBody();
            paramMap = JSONObject.parseObject(body, HashMap.class);
        } else {
            Map<String, String[]> parameterMap = requestWrapper.getParameterMap();
            //普通的GET请求
            if (parameterMap != null && parameterMap.size() > 0) {
                Set<Map.Entry<String, String[]>> entries = parameterMap.entrySet();
                for (Map.Entry<String, String[]> next : entries) {
                    paramMap.put(next.getKey(), next.getValue()[0]);
                }
            } else {
                //GET请求,参数在URL路径型式,比如server/{var1}/{var2}
                String afterDecodeUrl = null;
                try {
                    //编码过URL需解码解码还原字符
                    afterDecodeUrl = URLDecoder.decode(request.getRequestURI(), "UTF-8");
                } catch (UnsupportedEncodingException e) {
                    e.printStackTrace();
                }
                paramMap.put("pathVar", afterDecodeUrl);
            }
        }
        return paramMap;
    }

    // 效验
    protected static boolean sqlValidate(String str) {
        String s = str.toLowerCase();// 统一转为小写
        String badStr = "select|update|and|or|delete|insert|truncate|char|into|substr|ascii|declare|exec|count|master|into|drop|execute|table|"
                + "char|declare|sitename|xp_cmdshell|like|from|grant|use|group_concat|column_name|"
                + "information_schema.columns|table_schema|union|where|order|by|"
                + "'\\*|\\;|\\-|\\--|\\+|\\,|\\//|\\/|\\%|\\#";// 过滤掉的sql关键字，特殊字符前面需要加\\进行转义
        // 使用正则表达式进行匹配
        boolean matches = s.matches(badStr);
        return matches;
    }


    private boolean isSqlInject(String value, ServletResponse servletResponse) throws IOException {
        if (null != value && value.toLowerCase().matches(SQL_REG_EXP)) {
            logger.info("入参中有非法字符: " + value);
            HttpServletResponse response = (HttpServletResponse) servletResponse;
            Map<String, String> responseMap = new HashMap<>();
            // 匹配到非法字符,立即返回
            responseMap.put("code", "999");
            responseMap.put("message","入参中有非法字符");
            response.setContentType("application/json;charset=UTF-8");
            response.setStatus(HttpStatus.OK.value());
            response.getWriter().write(JSON.toJSONString(responseMap));
            response.getWriter().flush();
            response.getWriter().close();
            return false;
        }
        return true;
    }

    @Override
    public void init(FilterConfig filterConfig) throws ServletException {

    }

    @Override
    public void destroy() {

    }

    public static void main(String[] args) {
        boolean flag = "update a".matches(SQL_REG_EXP);
        System.out.println(flag);
    }

}
